import { uploadFileApi, uploadChunkApi, verifyApi, mergeApi } from '@/api/file'
import { createChunksHash, createFileChunks, createFileHash, formatSize, CHUNK_SIZE } from '@/utils/fileUtil'
import { ref, reactive, Ref } from 'vue'
import { UploadRawFile } from 'element-plus'

interface UploadFile {
  uid?: number // 文件的唯一ID
  name: string // 文件名称
  size?: number // 文件大小
  type?: string // 文件类型
  percent?: number // 上传完成百分比 -2: 待上传, -1: 准备中, 0: 准备就绪
  filePath?: string // 上传完之后的文件路径
  src?: string | ArrayBuffer, // 文件的预览地址
  file?: File // 要上传的文件
}

/**
 * by ssg 20240729
 文件分片上传：
    1. 创建文件分片
    2. 计算文件的hash
    3. 根据文件hash查询服务器上是否已存在这个文件，存在就不用上传，实现秒传功能
    4. 分片上传
    5. 文件合并
    6. 断点续传：上传分片之前先查询服务器上已上传的分片，只上传服务器上不存在的分片

  后端需要提供接口：
    1. 验证文件是否存在，只需要在 /verify 这个接口中加上已经上传成功的所有切片的名称就可以
       因为所有的切片都存放在以文件的hash值命名的那个文件夹
       所以需要读取这个文件夹中所有的切片的名称就可以。
    2. 文件分片上传接口
    3. 文件合并接口
 */
export const useUploader = (bucket: Ref<string>, path: Ref<string>, multiple = false) => {
  const chunkSize = CHUNK_SIZE
  const files = reactive<UploadFile[]>([])
  const allCompleted = ref(false)

  const beforeUpload = (file: UploadRawFile) => {
    if(!multiple) clear()

    const { uid, name, type, size } = file
    files.push({
      uid, name, type, size,
      percent: -2,
      file
    })
    return false
  }

  const upload = async () => {
    for(const i in files) {
      const file = files[i]
      if(file.filePath) continue

      if(file.size > chunkSize) {
        await uploadChunks(file)
      } else {
        await uploadFile(file)
      }
    }
    allCompleted.value = true
  }

  const uploadFile = async (upFile: UploadFile) => {
    const { file } = upFile
    upFile.percent = -1
    const hash: string = await createFileHash(file) as string
    const formData = new FormData()
    formData.append('file', file)
    formData.append('hash', hash)
    formData.append('bucket', bucket.value) // 存储桶
    formData.append('path', path.value) // 上传路径

    upFile.percent = 0
    upFile.filePath = await uploadFileApi(formData)
    upFile.percent = 100
  }

  const uploadChunks = async (uploadFile: UploadFile) => {
    const { file } = uploadFile
    uploadFile.percent = -1
    const chunks = await createFileChunks(file, chunkSize)
    const hash: string = await createChunksHash(chunks, chunkSize) as string

    const params = { bucket: bucket.value, path: path.value, hash, fileName: file.name }
    //status: yes:已存在，no:不存在，part：已上传了一部份，all：已全部上传，但未合并
    const { status, chunkList = [], filePath } = await verifyApi(params, chunks.length)
    uploadFile.percent = 0
    switch(status) {
      case 'yes':
        console.info('秒传')
        uploadFile.percent = 100
        uploadFile.filePath = filePath
        break
      case 'no':
        await doUpload(uploadFile, chunks, hash)
        uploadFile.filePath = await merge(hash, file.name)
        uploadFile.percent = 100
        break
      case 'part':
        await doUpload(uploadFile, chunks, hash, chunkList)
        uploadFile.filePath = await merge(hash, file.name)
        uploadFile.percent = 100
        break
      case 'all':
        uploadFile.percent = 99
        uploadFile.filePath = await merge(hash, file.name)
        uploadFile.percent = 100
        break
    }
  }

  const doUpload = async (uploadFile: UploadFile, chunks: Blob[], hash: string, uploadedList: number[] = []) => {
    const formDatas: FormData[] = []
    for (const index in chunks) {
      if (uploadedList.includes(Number(index))) continue

      const chunk = chunks[index]
      console.log('块大小：', chunk.size)
      const formData = new FormData()
      formData.append('file', chunk) // 切片文件
      formData.append('hash', hash) // 切片文件hash
      formData.append('bucket', bucket.value) // 存储桶
      formData.append('path', path.value) // 上传路径
      formData.append('chunkIndex', index)
      formDatas.push(formData)
    }

    const step = parseFloat((100 / formDatas.length).toFixed(10))
    let value = 0
    for(const i in formDatas) {
      await uploadChunkApi(formDatas[i])
      value += step
      uploadFile.percent = value > 99 ? 99 : parseFloat(value.toFixed(2))
    }
  }

  /**
   * 发请求通知服务器，合并切片
   */
  const merge = async (hash: string, fileName: string) => {
    const params = { bucket: bucket.value, path: path.value, hash, fileName }
    return await mergeApi(params)
  }

  const clear = () => files.length = 0

  return { beforeUpload, upload, clear, formatSize, files, allCompleted }
}
